/*
 * TaxaPanel.java
 *
 * Copyright (C) 2002-2006 Alexei Drummond and Andrew Rambaut
 *
 * This file is part of BEAST.
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * BEAST is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 *  BEAST is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with BEAST; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package dr.app.beauti;

import dr.evolution.util.*;
import org.virion.jam.framework.Exportable;
import org.virion.jam.panels.ActionPanel;
import org.virion.jam.table.TableRenderer;
import org.virion.jam.util.IconUtils;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.AbstractTableModel;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;

/**
 * @author			Andrew Rambaut
 * @author			Alexei Drummond
 * @version			$Id: TaxaPanel.java,v 1.1 2006/09/05 13:29:34 rambaut Exp $
 */
public class TaxaPanel extends JPanel implements Exportable {


	/**
	 *
	 */
	private static final long serialVersionUID = -3138832889782090814L;

	BeautiFrame frame = null;

	BeautiOptions options = null;

	private TaxonList taxa = null;
	private JTable taxonSetsTable = null;
	private TaxonSetsTableModel taxonSetsTableModel = null;

	private JPanel taxonSetEditingPanel = null;

	private Taxa currentTaxonSet = null;

	private List includedTaxa = new ArrayList();
	private List excludedTaxa = new ArrayList();

	private JTable excludedTaxaTable = null;
	private TaxaTableModel excludedTaxaTableModel = null;

	private JTable includedTaxaTable = null;
	private TaxaTableModel includedTaxaTableModel = null;

	private static int taxonSetCount = 0;

	public TaxaPanel(BeautiFrame parent) {

		this.frame = parent;

		Icon includeIcon = null, excludeIcon = null;
		try {
			includeIcon = new ImageIcon(IconUtils.getImage(this.getClass(), "images/include.png"));
			excludeIcon = new ImageIcon(IconUtils.getImage(this.getClass(), "images/exclude.png"));
		} catch (Exception e) {
			// do nothing
		}

		// Taxon Sets
		taxonSetsTableModel = new TaxonSetsTableModel();
		taxonSetsTable = new JTable(taxonSetsTableModel);
		taxonSetsTable.getColumnModel().getColumn(0).setCellRenderer(
				new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
		taxonSetsTable.getColumnModel().getColumn(0).setMinWidth(20);

		taxonSetsTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent evt) { taxonSetsTableSelectionChanged(); }
		});

		JScrollPane scrollPane1 = new JScrollPane(taxonSetsTable,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		ActionPanel actionPanel1 = new ActionPanel(false);
		actionPanel1.setAddAction(addTaxonSetAction);
		actionPanel1.setRemoveAction(removeTaxonSetAction);

		addTaxonSetAction.setEnabled(false);
		removeTaxonSetAction.setEnabled(false);

		JPanel controlPanel1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
		controlPanel1.add(actionPanel1);

		// Excluded Taxon List
		excludedTaxaTableModel = new TaxaTableModel(false);
		excludedTaxaTable = new JTable(excludedTaxaTableModel);

		excludedTaxaTable.getColumnModel().getColumn(0).setCellRenderer(
				new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
		excludedTaxaTable.getColumnModel().getColumn(0).setMinWidth(20);

		excludedTaxaTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent evt) { excludedTaxaTableSelectionChanged(); }
		});

		JScrollPane scrollPane2 = new JScrollPane(excludedTaxaTable,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		JPanel buttonPanel = createAddRemoveButtonPanel(includeTaxonAction, includeIcon, "Include selected taxa in the taxon set",
				excludeTaxonAction, excludeIcon, "Exclude selected taxa from the taxon set",
				javax.swing.BoxLayout.Y_AXIS);

		// Included Taxon List
		includedTaxaTableModel = new TaxaTableModel(true);
		includedTaxaTable = new JTable(includedTaxaTableModel);

		includedTaxaTable.getColumnModel().getColumn(0).setCellRenderer(
				new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4)));
		includedTaxaTable.getColumnModel().getColumn(0).setMinWidth(20);

		includedTaxaTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
			public void valueChanged(ListSelectionEvent evt) { includedTaxaTableSelectionChanged(); }
		});
		includedTaxaTable.doLayout();

		JScrollPane scrollPane3 = new JScrollPane(includedTaxaTable,
				JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
				JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

		taxonSetEditingPanel = new JPanel();
		taxonSetEditingPanel.setBorder(BorderFactory.createTitledBorder("Taxon Set: none selected"));
		taxonSetEditingPanel.setOpaque(false);
		taxonSetEditingPanel.setLayout(new GridBagLayout());
		GridBagConstraints c = new GridBagConstraints();

		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0.5;
		c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(12,12,12,0);
		taxonSetEditingPanel.add(scrollPane2, c);

		c.gridx = 1;
		c.gridy = 0;
		c.weightx = 0;
		c.weighty = 1;
		c.fill = GridBagConstraints.NONE;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(12,2,12,4);
		taxonSetEditingPanel.add(buttonPanel, c);

		c.gridx = 2;
		c.gridy = 0;
		c.weightx = 0.5;
		c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(12,0,12,12);
		taxonSetEditingPanel.add(scrollPane3, c);

		JPanel panel1 = new JPanel();
		panel1.setOpaque(false);
		panel1.setLayout(new GridBagLayout());
		c = new GridBagConstraints();

		c.gridx = 0;
		c.gridy = 0;
		c.weightx = 0.4;
		c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(0,0,2,12);
		panel1.add(scrollPane1, c);

		c.gridx = 0;
		c.gridy = 1;
		c.weightx = 0;
		c.weighty = 0;
		c.fill = GridBagConstraints.NONE;
		c.anchor = GridBagConstraints.WEST;
		c.insets = new Insets(2,0,0,12);
		panel1.add(actionPanel1, c);

		c.gridx = 1;
		c.gridy = 0;
		c.weightx = 0.6;
		c.weighty = 1;
		c.fill = GridBagConstraints.BOTH;
		c.anchor = GridBagConstraints.CENTER;
		c.insets = new Insets(0,0,0,0);
		panel1.add(taxonSetEditingPanel, c);

		setOpaque(false);
		setBorder(new BorderUIResource.EmptyBorderUIResource(new Insets(12, 12, 12, 12)));
		setLayout(new BorderLayout(0,0));
		add(panel1, BorderLayout.CENTER);

//		taxonSetsTable.addMouseListener(new MouseAdapter() {
//			public void mouseClicked(MouseEvent e) {
//				if (e.getClickCount() == 2) {
//					JTable target = (JTable)e.getSource();
//					int row = target.getSelectedRow();
//					taxonSetsTableDoubleClicked(row);
//				}
//			}
//		});

		includedTaxaTable.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					includeSelectedTaxa();
				}
			}
		});
		excludedTaxaTable.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				if (e.getClickCount() == 2) {
					excludeSelectedTaxa();
				}
			}
		});

		includedTaxaTable.addFocusListener(new FocusAdapter() {
			public void focusGained(FocusEvent focusEvent) {
				excludedTaxaTable.clearSelection();
			}
		});
		excludedTaxaTable.addFocusListener(new FocusAdapter() {
			public void focusGained(FocusEvent focusEvent) {
				includedTaxaTable.clearSelection();
			}
		});

		taxonSetsTable.doLayout();
		includedTaxaTable.doLayout();
		excludedTaxaTable.doLayout();
	}

	private void taxonSetChanged() {
		currentTaxonSet.removeAllTaxa();
		for (int i = 0; i < includedTaxa.size(); i++) {
			currentTaxonSet.addTaxon((Taxon)includedTaxa.get(i));
		}
		frame.taxonSetsChanged();
	}

	public void setOptions(BeautiOptions options) {

		this.options = options;

		taxa = options.alignment;
		if (taxa == null) {
			addTaxonSetAction.setEnabled(false);
			removeTaxonSetAction.setEnabled(false);
		} else {
			addTaxonSetAction.setEnabled(true);
		}

		taxonSetsTableSelectionChanged();
		taxonSetsTableModel.fireTableDataChanged();
	}

	public void getOptions(BeautiOptions options) {
//		options.datesUnits = unitsCombo.getSelectedIndex();
//		options.datesDirection = directionCombo.getSelectedIndex();
//		options.translation = translationCombo.getSelectedIndex();
	}

	public JComponent getExportableComponent() {
		return taxonSetsTable;
	}

	private void taxonSetsTableSelectionChanged() {
		int[] rows = taxonSetsTable.getSelectedRows();
		if (rows.length == 0) {
			removeTaxonSetAction.setEnabled(false);
		} else if (rows.length == 1) {
			currentTaxonSet = (Taxa)options.taxonSets.get(rows[0]);
			setCurrentTaxonSet(currentTaxonSet);
			removeTaxonSetAction.setEnabled(true);
		} else {
			setCurrentTaxonSet(null);
			removeTaxonSetAction.setEnabled(true);
		}
	}

//	private void taxonSetsTableDoubleClicked(int row) {
//		currentTaxonSet = (Taxa)taxonSets.get(row);
//
//		Collections.sort(taxonSets);
//		taxonSetsTableModel.fireTableDataChanged();
//
//		setCurrentTaxonSet(currentTaxonSet);
//
//		int sel = taxonSets.indexOf(currentTaxonSet);
//		taxonSetsTable.setRowSelectionInterval(sel, sel);
//	}

	Action addTaxonSetAction = new AbstractAction("+") {

		/**
		 *
		 */
		private static final long serialVersionUID = 20273987098143413L;

		public void actionPerformed(ActionEvent ae) {
			taxonSetCount ++;
			currentTaxonSet = new Taxa("untitled" + taxonSetCount);

			options.taxonSets.add(currentTaxonSet);
			Collections.sort(options.taxonSets);

			taxonSetsTableModel.fireTableDataChanged();

			int sel = options.taxonSets.indexOf(currentTaxonSet);
			taxonSetsTable.setRowSelectionInterval(sel, sel);

			taxonSetChanged();
		}
	};

	Action removeTaxonSetAction = new AbstractAction("-") {

		/**
		 *
		 */
		private static final long serialVersionUID = 6077578872870122265L;

		public void actionPerformed(ActionEvent ae) {
			int row = taxonSetsTable.getSelectedRow();
			if (row != -1) {
				options.taxonSets.remove(row);
			}
			taxonSetChanged();

			taxonSetsTableModel.fireTableDataChanged();

			if (row >= options.taxonSets.size()) {
				row = options.taxonSets.size() - 1;
			}
			if (row >= 0) {
				taxonSetsTable.setRowSelectionInterval(row, row);
			} else {
				setCurrentTaxonSet(null);
			}
		}
	};

	private void setCurrentTaxonSet(Taxa taxonSet) {

		this.currentTaxonSet = taxonSet;

		includedTaxa.clear();
		excludedTaxa.clear();

		if (currentTaxonSet != null) {
			for (int i = 0; i < taxonSet.getTaxonCount(); i++) {
				includedTaxa.add(taxonSet.getTaxon(i));
			}
			Collections.sort(includedTaxa);

			for (int i = 0; i < taxa.getTaxonCount(); i++) {
				excludedTaxa.add(taxa.getTaxon(i));
			}
			excludedTaxa.removeAll(includedTaxa);
			Collections.sort(excludedTaxa);
		}

		setTaxonSetTitle();

		includedTaxaTableModel.fireTableDataChanged();
		excludedTaxaTableModel.fireTableDataChanged();
	}

	private void setTaxonSetTitle() {

		if (currentTaxonSet == null) {
			taxonSetEditingPanel.setBorder(BorderFactory.createTitledBorder(""));
			taxonSetEditingPanel.setEnabled(false);
		} else {
			taxonSetEditingPanel.setEnabled(true);
			taxonSetEditingPanel.setBorder(BorderFactory.createTitledBorder("Taxon Set: " + currentTaxonSet.getId()));
		}
	}


	class TaxonSetsTableModel extends AbstractTableModel {

		/**
		 *
		 */
		private static final long serialVersionUID = 3318461381525023153L;

		public TaxonSetsTableModel() {
		}

		public int getColumnCount() {
			return 1;
		}

		public int getRowCount() {
			if (options == null) return 0;
			return options.taxonSets.size();
		}

		public Object getValueAt(int row, int col) {
			return ((TaxonList)options.taxonSets.get(row)).getId();
		}

		public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
			((Taxa)options.taxonSets.get(rowIndex)).setId(aValue.toString());
			setTaxonSetTitle();
		}

		public boolean isCellEditable(int row, int col) {
			return true;
		}

		public String getColumnName(int column) {
			return "Taxon Sets";
		}

		public Class getColumnClass(int c) {return getValueAt(0, c).getClass();}
	};

	JPanel createAddRemoveButtonPanel(Action addAction, Icon addIcon, String addToolTip,
	                                  Action removeAction, Icon removeIcon, String removeToolTip, int axis) {

		JPanel buttonPanel = new JPanel();
		buttonPanel.setLayout(new BoxLayout(buttonPanel, axis));
		buttonPanel.setOpaque(false);
		JButton addButton = new JButton(addAction);
		if (addIcon != null) {
			addButton.setIcon(addIcon);
			addButton.setText(null);
		}
		addButton.setToolTipText(addToolTip);
		addButton.putClientProperty("JButton.buttonType", "toolbar");
		addButton.setOpaque(false);
		addAction.setEnabled(false);

		JButton removeButton = new JButton(removeAction);
		if (removeIcon != null) {
			removeButton.setIcon(removeIcon);
			removeButton.setText(null);
		}
		removeButton.setToolTipText(removeToolTip);
		removeButton.putClientProperty("JButton.buttonType", "toolbar");
		removeButton.setOpaque(false);
		removeAction.setEnabled(false);

		buttonPanel.add(addButton);
		buttonPanel.add(new JToolBar.Separator(new Dimension(6,6)));
		buttonPanel.add(removeButton);

		return buttonPanel;
	}

	private void excludedTaxaTableSelectionChanged() {
		if (excludedTaxaTable.getSelectedRowCount() == 0) {
			includeTaxonAction.setEnabled(false);
		} else {
			includeTaxonAction.setEnabled(true);
		}
	}

	private void includedTaxaTableSelectionChanged() {
		if (includedTaxaTable.getSelectedRowCount() == 0) {
			excludeTaxonAction.setEnabled(false);
		} else {
			excludeTaxonAction.setEnabled(true);
		}
	}

	private void includeSelectedTaxa() {
		int[] rows = excludedTaxaTable.getSelectedRows();

		List transfer = new ArrayList();

		for (int i = 0; i < rows.length; i++) {
			transfer.add(excludedTaxa.get(rows[i]));
		}

		includedTaxa.addAll(transfer);
		Collections.sort(includedTaxa);

		excludedTaxa.removeAll(includedTaxa);

		includedTaxaTableModel.fireTableDataChanged();
		excludedTaxaTableModel.fireTableDataChanged();

		includedTaxaTable.getSelectionModel().clearSelection();
		for (int i = 0; i < transfer.size(); i++) {
			Taxon taxon = (Taxon)transfer.get(i);
			int row = includedTaxa.indexOf(taxon);
			includedTaxaTable.getSelectionModel().addSelectionInterval(row, row);
		}

		taxonSetChanged();
	}

	private void excludeSelectedTaxa() {
		int[] rows = includedTaxaTable.getSelectedRows();

		List transfer = new ArrayList();

		for (int i = 0; i < rows.length; i++) {
			transfer.add(includedTaxa.get(rows[i]));
		}

		excludedTaxa.addAll(transfer);
		Collections.sort(excludedTaxa);

		includedTaxa.removeAll(excludedTaxa);

		includedTaxaTableModel.fireTableDataChanged();
		excludedTaxaTableModel.fireTableDataChanged();

		excludedTaxaTable.getSelectionModel().clearSelection();
		for (int i = 0; i < transfer.size(); i++) {
			Taxon taxon = (Taxon)transfer.get(i);
			int row = excludedTaxa.indexOf(taxon);
			excludedTaxaTable.getSelectionModel().addSelectionInterval(row, row);
		}

		taxonSetChanged();
	}

	Action includeTaxonAction = new AbstractAction("->") {
		/**
		 *
		 */
		private static final long serialVersionUID = 7510299673661594128L;

		public void actionPerformed(ActionEvent ae) {
			includeSelectedTaxa();
		}
	};

	Action excludeTaxonAction = new AbstractAction("<-") {

		/**
		 *
		 */
		private static final long serialVersionUID = 449692708602410206L;

		public void actionPerformed(ActionEvent ae) {
			excludeSelectedTaxa();
		}
	};

	class TaxaTableModel extends AbstractTableModel {

		/**
		 *
		 */
		private static final long serialVersionUID = -8027482229525938010L;
		boolean included;

		public TaxaTableModel(boolean included) {
			this.included = included;
		}

		public int getColumnCount() {
			return 1;
		}

		public int getRowCount() {
			if (currentTaxonSet == null) return 0;

			if (included) {
				return includedTaxa.size();
			} else {
				return excludedTaxa.size();
			}
		}

		public Object getValueAt(int row, int col) {

			if (included) {
				return ((Taxon)includedTaxa.get(row)).getId();
			} else {
				return ((Taxon)excludedTaxa.get(row)).getId();
			}
		}

		public boolean isCellEditable(int row, int col) {
			return false;
		}

		public String getColumnName(int column) {
			if (included) return "Included Taxa";
			else return "Excluded Taxa";
		}

		public Class getColumnClass(int c) {return getValueAt(0, c).getClass();}
	};

}
